//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package project_alarm_api_serv

import (
	"context"
	"fmt"
	"time"

	"atomgit.com/openlinksaas/api-server/api_common"
	"atomgit.com/openlinksaas/api-server/bg_runner/tasks"
	"atomgit.com/openlinksaas/api-server/dao"
	"atomgit.com/openlinksaas/api-server/dao/alarm_dao"
	"atomgit.com/openlinksaas/api-server/dao/issue_dao"
	"atomgit.com/openlinksaas/api-server/utils"
	"atomgit.com/openlinksaas/proto-gen-go.git/notices/notices_project"
	"atomgit.com/openlinksaas/proto-gen-go.git/project_alarm_api"
	"go.mongodb.org/mongo-driver/mongo"
)

type ProjectAlarmApiImpl struct {
	project_alarm_api.UnimplementedProjectAlarmApiServer
}

func (apiImpl *ProjectAlarmApiImpl) SetConfig(ctx context.Context, req *project_alarm_api.SetConfigRequest) (*project_alarm_api.SetConfigResponse, error) {
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_alarm_api.SetConfigResponse{
			Code:   project_alarm_api.SetConfigResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	//检查权限
	projectItem, _, roleItem, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_alarm_api.SetConfigResponse{
			Code:   project_alarm_api.SetConfigResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_alarm_api.SetConfigResponse{
			Code:   project_alarm_api.SetConfigResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}
	if projectItem.Closed {
		return &project_alarm_api.SetConfigResponse{
			Code:   project_alarm_api.SetConfigResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}
	if !(projectItem.OwnerUserId == sessionItem.UserId || roleItem.BasicInfo.Admin) {
		return &project_alarm_api.SetConfigResponse{
			Code:   project_alarm_api.SetConfigResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}
	//检查config的配置
	if req.Config.EnableIssueDependCheck {
		if req.Config.IssueDependHitValue == 0 || req.Config.IssueDependAlertValue == 0 || req.Config.IssueDependHitValue >= req.Config.IssueDependAlertValue {
			return &project_alarm_api.SetConfigResponse{
				Code:   project_alarm_api.SetConfigResponse_CODE_WRONG_CONFIG,
				ErrMsg: "工单依赖检查参数不合理",
			}, nil
		}
	}
	if req.Config.EnableIssueDelayCheck {
		if req.Config.IssueDelayHitValue >= req.Config.IssueDelayAlertValue {
			return &project_alarm_api.SetConfigResponse{
				Code:   project_alarm_api.SetConfigResponse_CODE_WRONG_CONFIG,
				ErrMsg: "工单过期检查参数不合理",
			}, nil
		}
	}
	if req.Config.EnableIssueReOpenCheck {
		if req.Config.IssueReOpenHitValue == 0 || req.Config.IssueReOpenAlertValue == 0 || req.Config.IssueReOpenHitValue >= req.Config.IssueReOpenAlertValue {
			return &project_alarm_api.SetConfigResponse{
				Code:   project_alarm_api.SetConfigResponse_CODE_WRONG_CONFIG,
				ErrMsg: "工单重新打开检查参数不合理",
			}, nil
		}
	}
	//获取老的配置参数
	oldConfigItem, err := alarm_dao.ConfigDao.Get(ctx, req.ProjectId)
	if err != nil {
		oldConfigItem = &alarm_dao.ConfigDbItem{}
	}

	nowTime := time.Now().UnixNano() / 1e6

	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)
	ret, err := dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
		err = alarm_dao.ConfigDao.Set(sessCtx, &alarm_dao.ConfigDbItem{
			ProjectId:              req.ProjectId,
			EnableIssueDependCheck: req.Config.EnableIssueDependCheck,
			IssueDependHitValue:    req.Config.IssueDependHitValue,
			IssueDependAlertValue:  req.Config.IssueDependAlertValue,
			EnableIssueDelayCheck:  req.Config.EnableIssueDelayCheck,
			IssueDelayHitValue:     req.Config.IssueDelayHitValue,
			IssueDelayAlertValue:   req.Config.IssueDelayAlertValue,
			EnableIssueReOpenCheck: req.Config.EnableIssueReOpenCheck,
			IssueReOpenHitValue:    req.Config.IssueReOpenHitValue,
			IssueReOpenAlertValue:  req.Config.IssueReOpenAlertValue,
		})
		if err != nil {
			return nil, err
		}
		return &project_alarm_api.SetConfigResponse{
			Code: project_alarm_api.SetConfigResponse_CODE_OK,
		}, nil
	})
	if err != nil {
		return nil, err
	}
	//记录事件
	go apiImpl.addSetConfigEvent(sessionItem, projectItem, nowTime)
	go func() {
		err = apiImpl.adjustTasks(oldConfigItem, req.Config)
		if err != nil {
			fmt.Println(err)
		}
	}()
	return ret.(*project_alarm_api.SetConfigResponse), nil
}

func (apiImpl *ProjectAlarmApiImpl) adjustTasks(oldCfg *alarm_dao.ConfigDbItem, newCfg *project_alarm_api.Config) error {
	nowTime := time.Now().UnixNano() / 1e6

	if oldCfg.EnableIssueDelayCheck != newCfg.EnableIssueDelayCheck || oldCfg.IssueDelayHitValue != newCfg.IssueDelayHitValue || oldCfg.IssueDelayAlertValue != newCfg.IssueDelayAlertValue {
		//删除现有警告
		err := alarm_dao.AlarmDao.RemoveByProject(context.Background(), oldCfg.ProjectId, uint32(project_alarm_api.ALARM_TYPE_ALARM_TYPE_ISSUE_DELAY_HIT))
		if err != nil {
			return err
		}
		err = alarm_dao.AlarmDao.RemoveByProject(context.Background(), oldCfg.ProjectId, uint32(project_alarm_api.ALARM_TYPE_ALARM_TYPE_ISSUE_DELAY_ALERT))
		if err != nil {
			return err
		}
		if newCfg.EnableIssueDelayCheck {
			//列出所有过期的issue，并重新生成任务
			issueItemList, err := issue_dao.IssueDao.ListByDeadLineTime(context.Background(), oldCfg.ProjectId)
			if err != nil {
				return err
			}
			for _, issueItem := range issueItemList {
				hitTaskId := fmt.Sprintf("%s:%d", issueItem.IssueId, uint32(project_alarm_api.ALARM_TYPE_ALARM_TYPE_ISSUE_DELAY_HIT))
				err = dao.BgTaskDao.RemoveTask(context.Background(), hitTaskId)
				if err != nil {
					return err
				}
				if issueItem.HasDeadLineTime {
					err = dao.BgTaskDao.Insert(context.Background(), hitTaskId, nowTime, issueItem.DeadLineTime+int64(newCfg.IssueDelayHitValue)*24*3600*1000, &tasks.IssueDelayCheck{
						ProjectId: issueItem.ProjectId,
						IssueId:   issueItem.IssueId,
						AlarmType: uint32(project_alarm_api.ALARM_TYPE_ALARM_TYPE_ISSUE_DELAY_HIT),
					})
					if err != nil {
						return err
					}
				}

				alertTaskId := fmt.Sprintf("%s:%d", issueItem.IssueId, uint32(project_alarm_api.ALARM_TYPE_ALARM_TYPE_ISSUE_DELAY_ALERT))
				err = dao.BgTaskDao.RemoveTask(context.Background(), alertTaskId)
				if err != nil {
					return err
				}
				if issueItem.HasDeadLineTime {
					err = dao.BgTaskDao.Insert(context.Background(), alertTaskId, nowTime, issueItem.DeadLineTime+int64(newCfg.IssueDelayAlertValue)*24*3600*1000, &tasks.IssueDelayCheck{
						ProjectId: issueItem.ProjectId,
						IssueId:   issueItem.IssueId,
						AlarmType: uint32(project_alarm_api.ALARM_TYPE_ALARM_TYPE_ISSUE_DELAY_ALERT),
					})
					if err != nil {
						return err
					}
				}
			}
		}
	}
	if oldCfg.EnableIssueDependCheck != newCfg.EnableIssueDependCheck || oldCfg.IssueDependHitValue != newCfg.IssueDependHitValue || oldCfg.IssueDependAlertValue != newCfg.IssueDependAlertValue {
		taskId := fmt.Sprintf("%s:checkIssueDepend", oldCfg.ProjectId)
		//删除现有警告
		err := alarm_dao.AlarmDao.RemoveByProject(context.Background(), oldCfg.ProjectId, uint32(project_alarm_api.ALARM_TYPE_ALARM_TYPE_ISSUE_DEPEND_HIT))
		if err != nil {
			return err
		}
		err = alarm_dao.AlarmDao.RemoveByProject(context.Background(), oldCfg.ProjectId, uint32(project_alarm_api.ALARM_TYPE_ALARM_TYPE_ISSUE_DEPEND_ALERT))
		if err != nil {
			return err
		}
		//删除现有任务
		err = dao.BgTaskDao.RemoveTask(context.Background(), taskId)
		if err != nil {
			return err
		}
		if newCfg.EnableIssueDependCheck {
			//新增任务
			err = dao.BgTaskDao.Insert(context.Background(), taskId, nowTime, nowTime, &tasks.ProjectIssueDependCheck{
				ProjectId: oldCfg.ProjectId,
			})
			if err != nil {
				return err
			}
		}
	}
	if oldCfg.EnableIssueReOpenCheck != newCfg.EnableIssueReOpenCheck || oldCfg.IssueReOpenHitValue != newCfg.IssueReOpenHitValue || oldCfg.IssueReOpenAlertValue != newCfg.IssueReOpenAlertValue {
		taskId := fmt.Sprintf("%s:checkIssueReOpen", oldCfg.ProjectId)
		//删除现有警告
		err := alarm_dao.AlarmDao.RemoveByProject(context.Background(), oldCfg.ProjectId, uint32(project_alarm_api.ALARM_TYPE_ALARM_TYPE_ISSUE_REOPEN_HIT))
		if err != nil {
			return err
		}
		err = alarm_dao.AlarmDao.RemoveByProject(context.Background(), oldCfg.ProjectId, uint32(project_alarm_api.ALARM_TYPE_ALARM_TYPE_ISSUE_REOPEN_ALERT))
		if err != nil {
			return err
		}
		//删除现有任务
		err = dao.BgTaskDao.RemoveTask(context.Background(), taskId)
		if err != nil {
			return err
		}
		if newCfg.EnableIssueReOpenCheck {
			//新增任务
			err = dao.BgTaskDao.Insert(context.Background(), taskId, nowTime, nowTime, &tasks.ProjectIssueReOpenCheck{
				ProjectId: oldCfg.ProjectId,
			})
			if err != nil {
				return err
			}
		}
	}
	//发送警告变动通知
	go api_common.SendNoticeToProjectScope(context.Background(), oldCfg.ProjectId, &notices_project.UpdateAlarmStatNotice{
		ProjectId: oldCfg.ProjectId,
	})
	return nil
}

func (apiImpl *ProjectAlarmApiImpl) GetConfig(ctx context.Context, req *project_alarm_api.GetConfigRequest) (*project_alarm_api.GetConfigResponse, error) {
	emptyConfig := &project_alarm_api.Config{}

	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_alarm_api.GetConfigResponse{
			Code:   project_alarm_api.GetConfigResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
			Config: emptyConfig,
		}, nil
	}
	//检查权限
	_, _, _, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_alarm_api.GetConfigResponse{
			Code:   project_alarm_api.GetConfigResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
			Config: emptyConfig,
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_alarm_api.GetConfigResponse{
			Code:   project_alarm_api.GetConfigResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
			Config: emptyConfig,
		}, nil
	} else if err != nil {
		return nil, err
	}

	cfgItem, err := alarm_dao.ConfigDao.Get(ctx, req.ProjectId)
	if err != nil {
		return &project_alarm_api.GetConfigResponse{
			Code:   project_alarm_api.GetConfigResponse_CODE_OK,
			Config: emptyConfig,
		}, nil
	}
	return &project_alarm_api.GetConfigResponse{
		Code:   project_alarm_api.GetConfigResponse_CODE_OK,
		Config: cfgItem.ToConfig(),
	}, nil
}

func (apiImpl *ProjectAlarmApiImpl) GetAlarmState(ctx context.Context, req *project_alarm_api.GetAlarmStateRequest) (*project_alarm_api.GetAlarmStateResponse, error) {
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_alarm_api.GetAlarmStateResponse{
			Code:   project_alarm_api.GetAlarmStateResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	//检查权限
	_, _, _, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_alarm_api.GetAlarmStateResponse{
			Code:   project_alarm_api.GetAlarmStateResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_alarm_api.GetAlarmStateResponse{
			Code:   project_alarm_api.GetAlarmStateResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}
	hitCount, err := alarm_dao.AlarmDao.Count(ctx, req.ProjectId, true, alarm_dao.ALARM_LEVEL_HIT)
	if err != nil {
		return nil, err
	}
	alertCount, err := alarm_dao.AlarmDao.Count(ctx, req.ProjectId, true, alarm_dao.ALARM_LEVEL_ALERT)
	if err != nil {
		return nil, err
	}
	return &project_alarm_api.GetAlarmStateResponse{
		Code:       project_alarm_api.GetAlarmStateResponse_CODE_OK,
		HitCount:   hitCount,
		AlertCount: alertCount,
	}, nil
}

func (apiImpl *ProjectAlarmApiImpl) ListAlarm(ctx context.Context, req *project_alarm_api.ListAlarmRequest) (*project_alarm_api.ListAlarmResponse, error) {
	emptyList := []*project_alarm_api.Alarm{}

	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_alarm_api.ListAlarmResponse{
			Code:      project_alarm_api.ListAlarmResponse_CODE_WRONG_SESSION,
			ErrMsg:    "会话不存在",
			AlarmList: emptyList,
		}, nil
	}
	//检查权限
	_, _, _, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_alarm_api.ListAlarmResponse{
			Code:      project_alarm_api.ListAlarmResponse_CODE_NO_PROJECT,
			ErrMsg:    "项目不存在",
			AlarmList: emptyList,
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_alarm_api.ListAlarmResponse{
			Code:      project_alarm_api.ListAlarmResponse_CODE_NO_PERMISSION,
			ErrMsg:    "没有权限",
			AlarmList: emptyList,
		}, nil
	} else if err != nil {
		return nil, err
	}

	filterLevel := true
	level := alarm_dao.ALARM_LEVEL_HIT
	if req.IncludeHit && !req.IncludeAlert {
		filterLevel = true
		level = alarm_dao.ALARM_LEVEL_HIT
	} else if !req.IncludeHit && req.IncludeAlert {
		filterLevel = true
		level = alarm_dao.ALARM_LEVEL_ALERT
	} else {
		filterLevel = false
	}

	count, err := alarm_dao.AlarmDao.Count(ctx, req.ProjectId, filterLevel, uint32(level))
	if err != nil {
		return nil, err
	}
	alarmItemList, err := alarm_dao.AlarmDao.List(ctx, req.ProjectId, filterLevel, uint32(level), int64(req.Offset), int64(req.Limit))
	if err != nil {
		return nil, err
	}

	alarmList := []*project_alarm_api.Alarm{}
	for _, alarmItem := range alarmItemList {
		alarm, err := alarmItem.ToAlarm()
		if err != nil {
			continue
		}
		alarmList = append(alarmList, alarm)
	}

	//获取title
	issueIdList := []string{}
	for _, alarm := range alarmList {
		issueDependHitInfo, ok := alarm.Content.(*project_alarm_api.Alarm_IssueDependHitInfo)
		if ok {
			issueIdList = append(issueIdList, issueDependHitInfo.IssueDependHitInfo.IssueId)
		}
		issueDependAlertInfo, ok := alarm.Content.(*project_alarm_api.Alarm_IssueDependAlertInfo)
		if ok {
			issueIdList = append(issueIdList, issueDependAlertInfo.IssueDependAlertInfo.IssueId)
		}
		issueDelayHitInfo, ok := alarm.Content.(*project_alarm_api.Alarm_IssueDelayHitInfo)
		if ok {
			issueIdList = append(issueIdList, issueDelayHitInfo.IssueDelayHitInfo.IssueId)
		}
		issueDelayAlertInfo, ok := alarm.Content.(*project_alarm_api.Alarm_IssueDelayAlertInfo)
		if ok {
			issueIdList = append(issueIdList, issueDelayAlertInfo.IssueDelayAlertInfo.IssueId)
		}
		issueReOpenHitInfo, ok := alarm.Content.(*project_alarm_api.Alarm_IssueReOpenHitInfo)
		if ok {
			issueIdList = append(issueIdList, issueReOpenHitInfo.IssueReOpenHitInfo.IssueId)
		}
		issueReOpenAlertInfo, ok := alarm.Content.(*project_alarm_api.Alarm_IssueReOpenAlertInfo)
		if ok {
			issueIdList = append(issueIdList, issueReOpenAlertInfo.IssueReOpenAlertInfo.IssueId)
		}
	}
	issueTitleMap := map[string]string{}
	issueItemList, err := issue_dao.IssueDao.ListById(ctx, req.ProjectId, issueIdList)
	if err != nil {
		return nil, err
	}
	for _, issueItem := range issueItemList {
		issueTitleMap[issueItem.IssueId] = issueItem.BasicInfo.Title
	}

	//补充title
	for _, alarm := range alarmList {
		issueDependHitInfo, ok := alarm.Content.(*project_alarm_api.Alarm_IssueDependHitInfo)
		if ok {
			alarm.Title = issueTitleMap[issueDependHitInfo.IssueDependHitInfo.IssueId]
		}
		issueDependAlertInfo, ok := alarm.Content.(*project_alarm_api.Alarm_IssueDependAlertInfo)
		if ok {
			alarm.Title = issueTitleMap[issueDependAlertInfo.IssueDependAlertInfo.IssueId]
		}
		issueDelayHitInfo, ok := alarm.Content.(*project_alarm_api.Alarm_IssueDelayHitInfo)
		if ok {
			alarm.Title = issueTitleMap[issueDelayHitInfo.IssueDelayHitInfo.IssueId]
		}
		issueDelayAlertInfo, ok := alarm.Content.(*project_alarm_api.Alarm_IssueDelayAlertInfo)
		if ok {
			alarm.Title = issueTitleMap[issueDelayAlertInfo.IssueDelayAlertInfo.IssueId]
		}
		issueReOpenHitInfo, ok := alarm.Content.(*project_alarm_api.Alarm_IssueReOpenHitInfo)
		if ok {
			alarm.Title = issueTitleMap[issueReOpenHitInfo.IssueReOpenHitInfo.IssueId]
		}
		issueReOpenAlertInfo, ok := alarm.Content.(*project_alarm_api.Alarm_IssueReOpenAlertInfo)
		if ok {
			alarm.Title = issueTitleMap[issueReOpenAlertInfo.IssueReOpenAlertInfo.IssueId]
		}
	}

	return &project_alarm_api.ListAlarmResponse{
		Code:       project_alarm_api.ListAlarmResponse_CODE_OK,
		TotalCount: count,
		AlarmList:  alarmList,
	}, nil
}

func (apiImpl *ProjectAlarmApiImpl) RemoveAlarm(ctx context.Context, req *project_alarm_api.RemoveAlarmRequest) (*project_alarm_api.RemoveAlarmResponse, error) {
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_alarm_api.RemoveAlarmResponse{
			Code:   project_alarm_api.RemoveAlarmResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	//检查权限
	projectItem, _, roleItem, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_alarm_api.RemoveAlarmResponse{
			Code:   project_alarm_api.RemoveAlarmResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_alarm_api.RemoveAlarmResponse{
			Code:   project_alarm_api.RemoveAlarmResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}
	if projectItem.Closed {
		return &project_alarm_api.RemoveAlarmResponse{
			Code:   project_alarm_api.RemoveAlarmResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}
	if !(projectItem.OwnerUserId == sessionItem.UserId || roleItem.BasicInfo.Admin) {
		return &project_alarm_api.RemoveAlarmResponse{
			Code:   project_alarm_api.RemoveAlarmResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}
	exist, err := alarm_dao.AlarmDao.Exist(ctx, req.ProjectId, req.AlarmId)
	if err != nil {
		return nil, err
	}
	if !exist {
		return &project_alarm_api.RemoveAlarmResponse{
			Code:   project_alarm_api.RemoveAlarmResponse_CODE_NO_ALARM,
			ErrMsg: "预警不存在",
		}, nil
	}

	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)

	ret, err := dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
		err = alarm_dao.AlarmDao.Remove(sessCtx, req.ProjectId, req.AlarmId)
		if err != nil {
			return nil, err
		}
		return &project_alarm_api.RemoveAlarmResponse{
			Code: project_alarm_api.RemoveAlarmResponse_CODE_OK,
		}, nil
	})
	if err != nil {
		return nil, err
	}
	go api_common.SendNoticeToProjectScope(context.Background(), req.ProjectId, &notices_project.UpdateAlarmStatNotice{
		ProjectId: req.ProjectId,
	})
	return ret.(*project_alarm_api.RemoveAlarmResponse), nil
}
